/*
============================================================================
 Name        : dd_mp3_xing.c
 Author      : Jian
 Version     :
 Copyright   : GPL
 Description : dd_mp3_xing.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "dd_mp3_xing.h"
#include "dd_log.h"


// For Xing VBR Header flags
#define __FLAG_FRAMES     0x01
#define __FLAG_BYTES      0x02
#define __FLAG_TOC        0x04
#define __FLAG_VBR_SCALE  0x08

#define __TOC_LEN         100


static const DD_BYTE* _find_xing_header(const DD_BYTE* firstFrameBuf, int len) 
{
    const DD_BYTE* xingPos = firstFrameBuf + (4 + 2) ; 
    while(*xingPos == 0 && xingPos < (firstFrameBuf + len - 4)) {
        xingPos++ ;
    }

    if(memcmp(xingPos, "Xing", 4) == 0)
        return xingPos ;

    return NULL ;
}

static void _check_toc_value(dd_mp3_xing* xing)
{
    int i = 0 ;
    for (; i < (__TOC_LEN - 1); i++) {
        if (xing->toc[i] >= xing->toc[i+1]) {
            __DD_ASSERT(0) ;    /* TOC is not worked as what I imaged ?? */
            dd_destroy(xing->toc);
            return ;
        }
    }
}

dd_mp3_xing* dd_xing_create(const DD_BYTE* firstFrameBuf, int len) 
{
    dd_mp3_xing* xing = NULL ;
    const DD_BYTE*  loop = NULL;
    const DD_BYTE*  flag = NULL ;
    
    __DD_ASSERT_DEFENSE(loop && len >= (4 + 2 + 4), return NULL) ;
    
    xing = (dd_mp3_xing*)dd_malloc(sizeof(dd_mp3_xing)) ;
    __DD_CHECK_MEM(xing, sizeof(dd_mp3_xing), return NULL) ;

    loop = _find_xing_header(firstFrameBuf, len) ;    
    if (!loop) {
        dd_log_trace("not found xing header in first frame") ;
        return NULL ;
    }
    
    loop += 4 ;
    flag = loop ;
    loop += 4 ;
    if (flag[3] & __FLAG_FRAMES) {
        if ((loop + 4) >= (firstFrameBuf + len)) {
            dd_log_error("error to read frame count, that length of xing header is to small: %d", len) ;
            return NULL ;
        }
        xing->frameCount = dd_covert_endian32(loop)/* + 1 */;
        loop += 4 ;
    }

    if (flag[3] & __FLAG_BYTES) {
        if ((loop + 4) >= (firstFrameBuf + len)) {
            dd_log_error("error to read size, that length of xing header is to small: %d", len) ;
            return NULL ;
        }
        xing->fileSize = dd_covert_endian32(loop) ;
        loop += 4 ;
    }

    if (flag[3] & __FLAG_TOC) {
        if ((loop + __TOC_LEN) >= (firstFrameBuf + len)) {
            dd_log_error("error to read TOC, that length of xing header is to small: %d", len) ;
            return NULL ;
        }
        
        xing->toc = (DD_BYTE*)dd_malloc(sizeof(DD_BYTE) * __TOC_LEN) ;
        __DD_CHECK_MEM(xing->toc, sizeof(DD_BYTE) * __TOC_LEN, return NULL) ;
        memcpy(xing->toc, loop, __TOC_LEN) ;
        loop += __TOC_LEN ;
        _check_toc_value(xing) ;
    }

    if (flag[3] & __FLAG_VBR_SCALE) {
        if ((loop + 4) >= (firstFrameBuf + len)) {
            dd_log_error("error to read vbr scale, that length of xing header is to small: %d", len) ;
            return NULL ;
        }
        xing->vbrScale = dd_covert_endian32(loop);
    }

    return xing ;

    _error:
    if (xing)   dd_xing_destroy(xing) ;

    return NULL ;
}

void dd_xing_destroy(dd_mp3_xing* xing) 
{
    __DD_ASSERT_DEFENSE(xing, return) ;
    dd_destroy(xing->toc) ;
    dd_destroy(xing) ;
}


/* End of File */
